package net.scribblemedia.sqldsk.usercontroller;

import static java.lang.Integer.parseInt;
import static org.displaytag.tags.TableTagParameters.PARAMETER_EXPORTTYPE;
import static org.displaytag.tags.TableTagParameters.PARAMETER_PAGE;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import net.scribblemedia.sqldsk.domain.Comment;
import net.scribblemedia.sqldsk.domain.DatabaseConfig;
import net.scribblemedia.sqldsk.domain.ReportQuery;
import net.scribblemedia.sqldsk.domain.ReportRow;
import net.scribblemedia.sqldsk.domain.ReportRowState;
import net.scribblemedia.sqldsk.service.ReportRowService;
import net.scribblemedia.sqldsk.service.SqlPrettyPrinterService;
import net.scribblemedia.sqldsk.service.SqlReportRunnerService;

import org.displaytag.util.ParamEncoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@RequestMapping("/public/reports/**")
@Controller
public class ReportController {
	
	private static final int FIRST_PAGE = 1;

	private static final String PARAMETER_REPORT_QUERIES_PAGINATION_MAP = "reportQueriesPaginationMap";

	private static final Integer PAGE_SIZE = 40;
	
	private static final String PARAMETER_REPORT_QUERY_ID = "reportQueryId";
	private static final String PARAMETER_REPORT_ROW = "reportRow";
	private static final String PARAMETER_COMMENT = "comment";
	private static final String PARAMETER_DELETE_COMMENT = "deleteComment";
	private static final String PARAMETER_MARK_AS_NEW_STATE = "markAsNewState";
	
	private static final String BUTTON_MARK_AS_NEW_STATE = "markAsNewState";
	private static final String BUTTON_RESET_REPORT = "resetReport";
	private static final String BUTTON_RUN_REPORT = "runReport";
	private static final String BUTTON_COMMENT = "comment";
	private static final String BUTTON_DELETE_COMMENT = "deleteComment";
	private static final String BUTTON_SHOW_HIDE_ARCHIVED_ROWS = "showHideArchivedRows";
	private static final String BUTTON_TOGGLE_ARCHIVED_STATE = "toggleArchiveState";
	
	private static final String PAGE_PUBLIC_REPORTS = "public/reports";
	private static final String PARAMETER_PREFIX_REPORT_QUERY_TABLE_ID = "reportQueryTableId_";
	private static final String PARAMETER_DATABASE_CONFIGS = "databaseConfigs";
	
	@Autowired
	private SqlReportRunnerService sqlReportRunner;
	
	@Autowired
	private ReportRowService reportRowService;
	
    @Autowired
    private SqlPrettyPrinterService sqlPrettyPrinterService;
	
	@RequestMapping(method = RequestMethod.GET)
    public String handleGet(Model uiModel, ModelMap modelMap, HttpServletRequest request) {
		Map<Long, ReportQueryPaginationBean> paginationListMap = new HashMap<Long, ReportQueryPaginationBean>(); 
		
		for (DatabaseConfig databaseConfig : DatabaseConfig.findAllDatabaseConfigs()) {
			for (ReportQuery reportQuery : databaseConfig.getReportQueries()) {
				String displayTagPageParameterName = new ParamEncoder(getReportQueryDisplayTagTableId(reportQuery)).encodeParameterName(PARAMETER_PAGE);
				String displayTagPageNumber = request.getParameter(displayTagPageParameterName);
				ReportQueryPaginationBean paginationBean = buildReportQueryPaginationBean(reportQuery, displayTagPageParameterName, displayTagPageNumber);
				paginationListMap.put(reportQuery.getId(), paginationBean);
				
				String displayTagExportParameterName = new ParamEncoder(getReportQueryDisplayTagTableId(reportQuery)).encodeParameterName(PARAMETER_EXPORTTYPE);
				String displayTagExportPageNumber = request.getParameter(displayTagExportParameterName);
				
				//if this is the report query being exported, replace the partial list with the complete list.
				if (displayTagExportPageNumber != null) {
					paginationBean.setPartialList(reportQuery.getReportRows());
				}
			}
		}
		
		modelMap.put(PARAMETER_REPORT_QUERIES_PAGINATION_MAP, paginationListMap);
		modelMap.put(PARAMETER_DATABASE_CONFIGS, DatabaseConfig.findAllDatabaseConfigs());
		
        return PAGE_PUBLIC_REPORTS;
    }

	private ReportQueryPaginationBean buildReportQueryPaginationBean(ReportQuery reportQuery, String displayTagPageParameterName, String displayTagPageNumber) {
		int pageNumber = displayTagPageNumber == null ? FIRST_PAGE : parseInt(displayTagPageNumber);
		int startIndex = (pageNumber - 1) * PAGE_SIZE;
		
		int totalCount = (int) reportQuery.countReportRows(reportQuery.isShowArchivedRows()).longValue();
		List<ReportRow> partialList = reportQuery.getReportRows(startIndex, reportQuery.isShowArchivedRows(), PAGE_SIZE);
		
		return new ReportQueryPaginationBean(
						partialList,
						totalCount,
						PAGE_SIZE,
						startIndex + 1,
						pageNumber,
						commaDelimitedIdList(partialList),
						displayTagPageParameterName,
						sqlPrettyPrinterService.prettyPrint(reportQuery.getReportSql()),
						formatExportFilename(reportQuery.getExportFilename()));
	}

	private String formatExportFilename(String exportFilename) {
		if (exportFilename == null) {
			return "report";
		}
		
		return exportFilename.replace("$DATE$", new SimpleDateFormat("dd.MM.yyyy").format(new Date()));
	}

	private String getReportQueryDisplayTagTableId(ReportQuery reportQuery) {
		return PARAMETER_PREFIX_REPORT_QUERY_TABLE_ID + reportQuery.getId();
	}
	
	private String commaDelimitedIdList(List<ReportRow> partialList) {
		StringBuffer buf = new StringBuffer();
		int i = 0;
		for (ReportRow reportRow : partialList) {
			buf.append(reportRow.getId());
			i++;
			
			if (i < partialList.size()) {
				buf.append(",");
			}
		}
		
		return buf.toString();
	}

	@RequestMapping(params=BUTTON_RUN_REPORT, method = RequestMethod.POST)
    public String runReport(Model uiModel, ModelMap modelMap, Long databaseConfigId) {
		sqlReportRunner.runReportQuery(DatabaseConfig.findDatabaseConfig(databaseConfigId));
		return populateModelMapAndRedirectToReportPage(modelMap);
    }
	
	@RequestMapping(params=BUTTON_RESET_REPORT, method = RequestMethod.POST)
    public String resetReport(Model uiModel, ModelMap modelMap, Long databaseConfigId) {
		sqlReportRunner.clearReports(DatabaseConfig.findDatabaseConfig(databaseConfigId));
		return populateModelMapAndRedirectToReportPage(modelMap);
    }
	
	@RequestMapping(params=BUTTON_DELETE_COMMENT, method = RequestMethod.POST)
    public String deleteComment(Model uiModel, ModelMap modelMap, HttpServletRequest request) {
		Comment comment = Comment.findComment(Long.parseLong(request.getParameter(PARAMETER_DELETE_COMMENT)));
		reportRowService.deleteComment(comment);
		return populateModelMapAndRedirectToReportPage(modelMap);
    }
	
	@RequestMapping(params=BUTTON_SHOW_HIDE_ARCHIVED_ROWS, method = RequestMethod.POST)
    public String showHideArchivedRows(Model uiModel, ModelMap modelMap, HttpServletRequest request) {
		Long reportQueryId = Long.parseLong(request.getParameter(PARAMETER_REPORT_QUERY_ID));
		ReportQuery reportQuery = ReportQuery.findReportQuery(reportQueryId);
		reportRowService.toggleShowHideArchivedRows(reportQuery);
		return populateModelMapAndRedirectToReportPage(modelMap);
    }
	
	@RequestMapping(params=BUTTON_MARK_AS_NEW_STATE, method = RequestMethod.POST)
    public String markAsNewState(Model uiModel, ModelMap modelMap, HttpServletRequest request) {
		String newState = request.getParameter(PARAMETER_MARK_AS_NEW_STATE);
		ReportRowState reportRowState = ReportRowState.valueOf(newState);
		String reportRowId = request.getParameter(PARAMETER_REPORT_ROW);
		ReportRow reportRow = ReportRow.findReportRow(Long.parseLong(reportRowId));
		reportRowService.updateReportRowState(reportRow, reportRowState);
		return populateModelMapAndRedirectToReportPage(modelMap);
    }
	
	@RequestMapping(params=BUTTON_TOGGLE_ARCHIVED_STATE, method = RequestMethod.POST)
    public String toggleArchivedState(Model uiModel, ModelMap modelMap, HttpServletRequest request) {
		String reportRowId = request.getParameter(PARAMETER_REPORT_ROW);
		ReportRow reportRow = ReportRow.findReportRow(Long.parseLong(reportRowId));
		reportRowService.toggleArchivedState(reportRow);
		return populateModelMapAndRedirectToReportPage(modelMap);
    }
	
	@RequestMapping(params=BUTTON_COMMENT, method = RequestMethod.POST)
    public String comment(Model uiModel, ModelMap modelMap, HttpServletRequest request) {
		String reportRowId = request.getParameter(PARAMETER_REPORT_ROW);
		ReportRow reportRow = ReportRow.findReportRow(Long.parseLong(reportRowId));
		String commentText = request.getParameter(PARAMETER_COMMENT);
		reportRowService.addComment(reportRow, commentText);
		return populateModelMapAndRedirectToReportPage(modelMap);
    }

	private String populateModelMapAndRedirectToReportPage(ModelMap modelMap) {
		modelMap.put(PARAMETER_DATABASE_CONFIGS, DatabaseConfig.findAllDatabaseConfigs());
        return "redirect:/" + PAGE_PUBLIC_REPORTS;
	}
} 